SpringBoot 版本区别 SpringBoot 2.0.x 和 SpringBoot 2.1.x 使用不同:SpringBoot 2.1.x 版本中书写数据库驱动的和数据路径和 2.0.x 中不一样。 2.1.x 中使用的 mysql 驱动是 com.mysql.cj.jdbc.Driver,并在 url 后面需要配置 ?serverTimezone=GMT%2B8 来解决时区问题。
SpringBoot 1.x 和 SpringBoot 2.x 的区别:SpringBoot 1.x 底层使用的是 Spring 4.x,而 SpringBoot 2.x 底层使用的是 Spring 5.x。Spring 5.x 中的注解功能比 Spring 4.x 中的更加强大。
MP 简单示例 代码地址
官网:http://mp.baomidou.com/,参考教程:http://mp.baomidou.com/guide/,[MyBatis-Plus](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
创建数据库及表信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 CREATE TABLE user ( id BIGINT (20 ) NOT NULL COMMENT '主键ID' , name VARCHAR (30 ) NULL DEFAULT NULL COMMENT '姓名' , age INT (11 ) NULL DEFAULT NULL COMMENT '年龄' , email VARCHAR (50 ) NULL DEFAULT NULL COMMENT '邮箱' , PRIMARY KEY (id ) ); INSERT INTO user (id , name , age, email) VALUES (1 , 'Jone' , 18 , 'test1@baomidou.com' ), (2 , 'Jack' , 20 , 'test2@baomidou.com' ), (3 , 'Tom' , 28 , 'test3@baomidou.com' ), (4 , 'Sandy' , 21 , 'test4@baomidou.com' ), (5 , 'Billie' , 24 , 'test5@baomidou.com' );
创建 mbp-demo 项目,并引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--lombok用来简化实体类--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
添加配置文件
1 2 3 4 5 6 7 # 配置连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123456 # 打印日志(例如 SQL 语句) mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
在 IDEA 中安装 lombok 插件, 并测试查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.yanrs.mpbdemo;import com.yanrs.mpbdemo.entity.User;import com.yanrs.mpbdemo.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith (SpringRunner.class ) @SpringBootTest class MpbDemoApplicationTests { @Autowired private UserMapper userMapper; @Test void testSelectList () { List<User> users = userMapper.selectList(null ); users.forEach(System.out::println); } }
主键生成策略 分布式系统唯一ID生成方案汇总: https://www.cnblogs.com/haoxinyue/p/5208136.html
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID 【也就是上面链接中提到的 snowflake 算法生成的】。如果想手动指定,那么可以在实体类的需要应用的字段上进行设置。
1 2 3 4 5 6 7 8 @Data public class User { @TableId (type = IdType.ID_WORKER) private Long id; private String name; private Integer age; private String email; }
上面的设置只是针对一个实体类进行设置的,如果要进行全局的设置,那么可以在配置文件中进行如下配置
1 mybatis-plus.global-config.db-config.id-type=auto
IdType 的取值,也就是设置主键的 ID 有以下几种方案:
1 2 3 4 5 6 AUTO 自动增长 NONE 为空 INPUT 需要手工输入,不是自动生成 ID_WORKER snowflake算法生成的ID UUID UUID 生成的ID ID_WORKER_STR snowflake算法生成的字符串ID
插入示例
1 2 3 4 5 6 7 8 9 10 @Test void addUser () { User user = new User(); user.setAge(10 ); user.setEmail("zhangsan@gmail.com" ); user.setName("zhangsan" ); int insert = userMapper.insert(user); System.out.println(insert); }
自动填充 数据库表结构新增 create_time,update_time 两个字段,默认值为 null。如果数据库中这两个字断没有设置默认值,那么数据的插入和修改的时候这两个值都是空的。我们可以使用 mp 来指定字段,在 insert 或者 update 的时候进行值的填充。
实体类上添加注解 @TableField
表明是在什么时候进行填充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Data public class User { @TableId (type = IdType.ID_WORKER) private Long id; private String name; private Integer age; private String email; @TableField (fill = FieldFill.INSERT) private Date createTime; @TableField (fill = FieldFill.INSERT_UPDATE) private Date updateTime; }
新建 handler 包,并新建一个类 customMpMateObjectHandler
实现 MetaObjectHandler
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.yanrs.mpbdemo.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Component public class customMpMateObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("createTime" , new Date(), metaObject); this .setFieldValByName("updateTime" , new Date(), metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("updateTime" , new Date(), metaObject); } }
再次运行 insert 新增一个用户,就会发现数据库中 create_time,update_time 两个字段都有了值。修改也是 update_time 的值也发生了变化。
1 2 3 4 5 6 7 8 9 10 @Test void addUser () { User user = new User(); user.setAge(10 ); user.setEmail("lisi@gmail.com" ); user.setName("lisi" ); int insert = userMapper.insert(user); System.out.println(insert); }
1 2 3 4 5 6 7 8 9 @Test void updateUser () { User user = new User(); user.setId(1271700428140089345L ); user.setName("new name" ); int i = userMapper.updateById(user); System.out.println(i); }
乐观锁 主要适用场景:当更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新。乐观锁解决的是 “丢失更新” 的问题。“丢失更新” 是指,事务中当对同一条记录进行修改的时候(事务中对同一条记录进行读可能出现脏读,不可重复读和幻读,写则可能出现丢失更新),例如 A 将数据库中的值修改为 300,然后提交了事务,同时 B 将又将数据库中的值改为 500,也提交了事务。最后数据库中的值是 500 ,但是对于 A 来说,就是 “丢失更新 ”。
解决丢失更新的方案是使用乐观锁,在数据库中新增 version 字段,A,B 在进行更改前获取该 version 字段的值,提交事务的时候判断自己获取的 version 值和数据库中的是否一致,一致则提交事务,并将 version 值加一,否则本次修改失败。整个过程如下:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
数据库中新增 version 字段,并在实体类中新增 version 属性,使用自动填充当数据插入时设置 version 的默认值为1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.yanrs.mpbdemo.entity;import com.baomidou.mybatisplus.annotation.*;import lombok.Data;import java.util.Date;@Data public class User { @TableId (type = IdType.ID_WORKER) private Long id; private String name; private Integer age; private String email; @TableField (fill = FieldFill.INSERT) private Date createTime; @TableField (fill = FieldFill.INSERT_UPDATE) private Date updateTime; @TableField (fill = FieldFill.INSERT) @Version private Integer version; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.yanrs.mpbdemo.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Component public class customMpMateObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("createTime" , new Date(), metaObject); this .setFieldValByName("updateTime" , new Date(), metaObject); this .setFieldValByName("version" , 1 , metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("updateTime" , new Date(), metaObject); } }
新增 MP 乐观锁插件的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.yanrs.mpbdemo.config;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan ("com.yanrs.mpbdemo.mapper" )@Configuration @EnableTransactionManagement public class MybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } }
新增一条数据,然后对其进行修改操作,测试数据库中 version 字段的变化。会发现 version 字段的值会从 1 变成2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test void addUser () { User user = new User(); user.setAge(10 ); user.setEmail("wangwu@gmail.com" ); user.setName("wangwu" ); int insert = userMapper.insert(user); System.out.println(insert); } @Test void testOptimisticLocker () { User user = userMapper.selectById(1271729094970646529L ); user.setName("new Name" ); int i = userMapper.updateById(user); System.out.println(i); }
简单查询 根据 ID 进行查询
1 2 3 4 5 @Test void testSelectById () { User user = userMapper.selectById(1271729094970646529L ); System.out.println(user); }
批量查询
1 2 3 4 5 @Test void testSelectBatchIds () { List<User> users = userMapper.selectBatchIds(Arrays.asList(1271729094970646529L , 1271700428140089345L )); System.out.println(users); }
根据 Map 进行查询
1 2 3 4 5 6 7 8 9 @Test void testSelectByMap () { HashMap<String, Object> condition = new HashMap<>(); condition.put("name" , "zhangsan" ); condition.put("create_time" , new Date()); List<User> users = userMapper.selectByMap(condition); System.out.println(users); }
分页查询 配置分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.yanrs.mpbdemo.config;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan ("com.yanrs.mpbdemo.mapper" )@Configuration @EnableTransactionManagement public class MybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); } }
分页方式1,返回的是查询的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testPageObject () { Page<User> page = new Page<>(1 , 3 ); userMapper.selectPage(page, null ); System.out.println(page.getCurrent()); System.out.println(page.getPages()); System.out.println(page.getRecords()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.hasNext()); System.out.println(page.hasPrevious()); }
分页方式2,返回的是 map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testPageMap () { Page<User> page = new Page<>(1 , 3 ); userMapper.selectMapsPage(page, null ); System.out.println(page.getCurrent()); System.out.println(page.getPages()); System.out.println(page.getRecords()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.hasNext()); System.out.println(page.hasPrevious()); }
简单删除 根据 ID 删除记录
1 2 3 4 @Test void testDeleteById () { userMapper.deleteById(1L ); }
批量删除
1 2 3 4 @Test void testDeleteBatchIds () { userMapper.deleteBatchIds(Arrays.asList(2L , 3L )); }
条件删除
1 2 3 4 5 6 @Test void testDeleteByMap () { HashMap<String, Object> condition = new HashMap<>(); condition.put("name" , "zhangsan" ); userMapper.deleteByMap(condition); }
逻辑删除 数据库新增 deleted 字段,并在实体类中新增 deleted 属性,配置该字段为逻辑删除字段,并在配置类中配置逻辑删除。
在实体类中新增 deleted 属性,配置该字段注解 TableLogic 表示为逻辑删除字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.yanrs.mpbdemo.entity;import com.baomidou.mybatisplus.annotation.*;import lombok.Data;import java.util.Date;@Data public class User { @TableId (type = IdType.ID_WORKER) private Long id; private String name; private Integer age; private String email; @TableField (fill = FieldFill.INSERT) private Date createTime; @TableField (fill = FieldFill.INSERT_UPDATE) private Date updateTime; @TableField (fill = FieldFill.INSERT) @Version private Integer version; @TableField (fill = FieldFill.INSERT) @TableLogic private Integer deleted; }
在插入数据的时候,自动为 deleted 字段设置值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.yanrs.mpbdemo.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Component public class customMpMateObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("createTime" , new Date(), metaObject); this .setFieldValByName("updateTime" , new Date(), metaObject); this .setFieldValByName("version" , 1 , metaObject); this .setFieldValByName("deleted" , 1 , metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("updateTime" , new Date(), metaObject); } }
在配置类中加上逻辑删除插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.yanrs.mpbdemo.config;import com.baomidou.mybatisplus.core.injector.ISqlInjector;import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan ("com.yanrs.mpbdemo.mapper" )@Configuration @EnableTransactionManagement public class MybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); } @Bean public ISqlInjector iSqlInjector () { return new LogicSqlInjector(); } }
在 properties 配置文件中配置逻辑删除和非逻辑删除的标识
1 2 3 # 逻辑删除 mybatis-plus.global-config.db-config.logic-delete-value=0 mybatis-plus.global-config.db-config.logic-not-delete-value=1
新增数据
1 2 3 4 5 6 7 8 9 10 @Test void addUser () { User user = new User(); user.setAge(10 ); user.setEmail("zhaoliu@gmail.com" ); user.setName("zhaoliu" ); int insert = userMapper.insert(user); System.out.println(insert); }
测试逻辑删除
1 2 3 4 5 6 @Test void testDeleteByMapLogic () { HashMap<String, Object> condition = new HashMap<>(); condition.put("name" , "zhaoliu" ); userMapper.deleteByMap(condition); }
注意⚠️:如果再运行查询,那么查询到的数据是没有被逻辑删除的,这是因为 MP 插件自动为我们处理了。如果要查询所有的数据(包含被逻辑删除了的和没有被逻辑删除了的)那么就得自己写 SQL 进行查询。
条件查询
查询方式
说明
setSqlSelect
设置 SELECT 查询字段
where
WHERE 语句,拼接 + WHERE 条件
and
AND 语句,拼接 + AND 字段=值
andNew
AND 语句,拼接 + AND (字段=值)
or
OR 语句,拼接 + OR 字段=值
orNew
OR 语句,拼接 + OR (字段=值)
eq
等于=
allEq
基于 map 内容等于=
ne
不等于<>
gt
大于>
ge
大于等于>=
lt
小于<
le
小于等于<=
like
模糊查询 LIKE
notLike
模糊查询 NOT LIKE
in
IN 查询
notIn
NOT IN 查询
isNull
NULL 值查询
isNotNull
IS NOT NULL
groupBy
分组 GROUP BY
having
HAVING 关键词
orderBy
排序 ORDER BY
orderAsc
ASC 排序 ORDER BY
orderDesc
DESC 排序 ORDER BY
exists
EXISTS 条件语句
notExists
NOT EXISTS 条件语句
between
BETWEEN 条件语句
notBetween
NOT BETWEEN 条件语句
addFilter
自由拼接 SQL
last
拼接在最后,例如:last(“LIMIT 1”)
例子
1 2 3 4 5 6 7 8 9 10 11 @Test void testQueryWrapper () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select("name" , "age" ); queryWrapper.eq("name" , "zhangsan" ); queryWrapper.gt("age" , 12 ); List<User> users = userMapper.selectList(queryWrapper); for (User user:users) { System.out.println(user); } }